package net.siufung.security.provider;

import com.google.common.collect.Lists;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.siufung.core.result.ResultFail;
import net.siufung.security.api.service.IAccessTokenService;
import net.siufung.security.base.CurrentUser;
import net.siufung.security.base.service.ICurrentUserService;
import net.siufung.security.provider.dto.AccessTokenLoginDto;
import net.siufung.security.provider.exception.AccessTokenAuthenticationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 陈建峰
 * @since 2022/3/2 2:47 下午
 */
@Slf4j
@SuppressWarnings("deprecation")
public class AccessTokenAuthenticationProvider implements AuthenticationProvider, InitializingBean {

    @Setter
    private IAccessTokenService accessTokenService;

    @Setter
    private ICurrentUserService currentUserService;

    @Setter
    private TokenStore tokenStore;

    @SneakyThrows
    @Override
    public void afterPropertiesSet() {
        if(Objects.isNull(currentUserService)){
            throw new AccessTokenAuthenticationException("currentUserService is null");
        }
        if(Objects.isNull(accessTokenService)){
            throw new AccessTokenAuthenticationException("accessTokenService is null");
        }
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        AccessTokenAuthenticationToken accessTokenOfAuthenticationToken =
                (AccessTokenAuthenticationToken) authentication;
        AccessTokenLoginDto accessTokenLoginDto = accessTokenOfAuthenticationToken.getAccessTokenLoginDto();
        String accessToken = accessTokenService.getValidToken(accessTokenLoginDto.getAccessToken());
        CurrentUser currentUser = currentUserService.getUserByValidToken(accessToken);
        if(!Objects.isNull(currentUser)){
            // 缓存情况下需要先清除Token
            if(!Objects.isNull(tokenStore)){
                tokenStore.removeAccessToken(tokenStore.readAccessToken(accessTokenLoginDto.getAccessToken()));
            }
            List<String> authorities = currentUserService.getAuthorities(currentUser.getUsername());
            List<GrantedAuthority> grantedAuthorityList = Lists.newArrayList();
            if(!CollectionUtils.isEmpty(authorities)){
                grantedAuthorityList = authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            }
            return this.createSuccessAuthentication(currentUser, authentication, grantedAuthorityList);
        }
        throw new AccessTokenAuthenticationException(ResultFail.AUTH_INVALID_TOKEN);
    }

    protected Authentication createSuccessAuthentication(
            CurrentUser currentUser,
            Authentication authentication,
            List<GrantedAuthority> grantedAuthorityList){
        AccessTokenAuthenticationToken result = new AccessTokenAuthenticationToken(
                currentUser,
                currentUser.getPassword(),
                grantedAuthorityList);
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return aClass.equals(AccessTokenAuthenticationToken.class);
    }
}
